#include "ViewPlane.h"

namespace RayTracer
{
    void ViewPlane::ResetEyePosition()
    {
	float horizMiddle = this->left + (this->right - this->left) / 2.0f;
	float vertMiddle = this->top + (this->bottom - this->top) / 2.0f;
	float focalDistance = 2.5f;
	this->perspectiveOrigin = Vector3D(horizMiddle, vertMiddle, focalDistance);
    }

    Vector3D ViewPlane::GetRayOrigin(const Vector3D *viewPlanePoint) const
    {
	if(this->projMode == Orthographic)
	{
	    return *viewPlanePoint;
	}
			
	return this->perspectiveOrigin;
    }
		
    Vector3D ViewPlane::GetRayDirection(const Vector3D *viewPlanePoint) const
    {
		if(this->projMode == Orthographic)
		{
			return this->orthoDir;
		}
			
		Vector3D diff = Vector3D::Subtract(viewPlanePoint, &this->perspectiveOrigin);
		return diff;
    }

    Vector3D ViewPlane::ScreenToViewPlane(float x, float y) const
    {
	Vector3D planePoint;

	float width = this->right - this->left;
	float height = this->top - this->bottom;

	return Vector3D(this->left + (width) * x / this->scrW,
			this->top - (height) * y / this->scrH,
			0.0f);
    }

    ViewPlane::ViewPlane(ProjectionMode projMode)
    {
	this->projMode = projMode;
	this->orthoDir = Vector3D(0.0f, 0.0f, -1.0f);
    }
		
    void ViewPlane::Resize(float w, float h)
    {
	this->scrW = w;
	this->scrH = h;

	float aspect = w / h;

	this->top = 1.0f;
	this->bottom = -1.0f;
	this->left = -1.0f;
	this->right = 1.0f;

	if (w <= h)
	{
	    this->left = -1.0f * aspect;
	    this->right = 1.0f * aspect;
	}
	else
	{
	    this->top = 1.0f / aspect;
	    this->bottom = -1.0f / aspect;
	}

	this->ResetEyePosition();
    }

    Ray3D ViewPlane::GetViewRay(float x, float y) const
    {
	Vector3D viewPlanePoint = this->ScreenToViewPlane(x, y);
			
	Vector3D origin = this->GetRayOrigin(&viewPlanePoint);
	Vector3D dir = this->GetRayDirection(&viewPlanePoint);
			
	Vector3D::Normalize(&dir);
	return Ray3D(origin, dir);
    }

    ProjectionMode ViewPlane::GetProjectionMode() const
    {
	return this->projMode;
    }
    
    void ViewPlane::SetProjectionMode(ProjectionMode value)
    {
	this->projMode = value;
    }
};
